package com.markhsiu.common.memory;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 内存池
 * 
 * @author Mark Hsiu 
 *         http://blog.csdn.net/shawngucas/article/details/6574863
 *         https://www.zhihu.com/question/25527491?sort=created
 *         https://github.com/alexkasko/unsafe-tools
 *         https://github.com/MyCATApache/Mycat-BigMemoryAPI
 *         http://calixwu.com/2014/11/memcached-yuanmafenxi-neicunguanli.html
 *         http://www.linuxidc.com/Linux/2016-04/129962.htm
 *         http://www.cnblogs.com/zhanglitong/p/4430927.html
 */
public class MemoryPool {

	private static final int DEFAULT_CAP = 128;// 内存大小128M
	private int capacity;//内存池容量
	private int remain;// 剩余大小
	private TreeSet<MMChunk> chunks;// 升序
	private UnsafeMemory buffer;
	private Lock lock = new ReentrantLock();

	public MemoryPool() {
		this(DEFAULT_CAP);
	}

	public MemoryPool(int capacity_m) {
		int newCapacity = capacity_m * 1024 * 1024;
		if(newCapacity % MMBlock.LENGTH_MAX > 0){
			newCapacity += MMBlock.LENGTH_MAX;
		}
		capacity = newCapacity;
		remain = newCapacity;
		this.init();
	}

	private void init() {
		buffer = new UnsafeMemory(capacity);
		chunks = new TreeSet<>(new ValueComparator());
		
		int multiple = capacity / MMChunk.LENGTH_MAX;
		int remainder = capacity % MMChunk.LENGTH_MAX;
		int offset = 0;
		for (int i = 0; i < multiple; i++) {
			chunks.add(new MMChunk().builder(this, offset, MMChunk.LENGTH_MAX));
			offset += MMChunk.LENGTH_MAX;
		}
		if(remainder > 0){
			chunks.add(new MMChunk().builder(this, offset, remainder));
		}	
	}
	
	
	public ByteBuff alloc(int length) {
		MMBlock[] newBlcoks;
		int newLength = length;
		int remainder = length % MMBlock.LENGTH_MAX;
		if(remainder > 0){
			newLength = length - remainder+ MMBlock.LENGTH_MAX;
		}
		
		int multiple = newLength / MMBlock.LENGTH_MAX;
		
		lock.lock();
		try {
			if(remain < length){
				throw new IllegalArgumentException("内存池不够");
			}
			newBlcoks = allocBlock(multiple);
			remain = remain - newLength;
		} finally {
			lock.unlock();
		}	
		return new PoolByteBuff(buffer,newBlcoks,newLength);
	}
	
	private MMBlock[] allocBlock(int num){
		int newNum = num;	
		Iterator<MMChunk> iter = chunks.iterator();
		MMBlock[] newBlcoks = new MMBlock[newNum];
		int pos = 0;
		MMBlock[] temp ;
		while(iter.hasNext()){
			MMChunk chunk = iter.next();
			int remain = chunk.remain();
			if(remain >= newNum){
				temp = chunk.alloc(newNum);
				System.arraycopy(temp, 0, newBlcoks, 0, newNum);
				break;
			}
			temp = chunk.alloc(remain);
			System.arraycopy(temp, 0, newBlcoks, pos, remain);
			newNum -= remain;
			pos += remain;
		}
		
		return newBlcoks;
	}
	
	public void free(ByteBuff buff) {
		lock.lock();
		
		try {
			if(buff instanceof  PoolByteBuff){
				((PoolByteBuff)buff).free();
			} else {
				throw new IllegalArgumentException("释放buffer类型错误");
			}
			
		} finally {
			lock.unlock();
		}	
	}
	
	void reStore(MMChunk chunk, int size){
		 remain += size;
		 chunks.remove(chunk);
		 chunks.add(chunk);
	}
	

	public void close(){
		remain = 0; 
		chunks = null;
		buffer.free();
	}

	public int remian(){
		return remain;
	}
	
	class ValueComparator implements Comparator<MMChunk> {

		@Override
		public int compare(MMChunk o1, MMChunk o2) {
			if (o1.remain() >= o2.remain()) {
				return 1;
			} else {
				return -1;
			}
		}
	}
 
}
